"""
    Author: Yun Mobei
    Modified: 2024-12-23
    Adapted from: https://github.com/israel-dryer/File-Search-Engine-Tk
"""
import random
import time
import threading
import json
import tkinter as tk
import ttkbootstrap as ttk
from tkinter import messagebox
from ttkbootstrap.constants import *
from ttkbootstrap.scrolled import ScrolledText
import SparkApi

class SparkAi():
    def __init__(self,master):
        self.root = master
        self.root.title(config_dict["proname"])
        self.root._style = ttk.Style(theme=config_dict["defaulttheme"])#重设主题
        self.root.geometry(config_dict["windowsize"])#self.root.geometry(f"{width}x{height}")
        self.pop_ai_tpl()
        
    def pop_ai_tpl(self):
        """弹出AI大模型使用窗口"""
        fr = ttk.Frame(self.root)
        fr.pack(fill=tk.BOTH,expand=tk.YES)
        nb = ttk.Notebook(fr, height=600, width=800)
        nb.pack(padx=5, pady=5, fill=tk.BOTH)
        
        tab1 = ttk.Frame(nb)
        self.create_ai_tab1(tab1)
        
        tab2 = ttk.Frame(nb)
        self.create_ai_tab2(tab2)
        
        tab3 = ttk.Frame(nb)
        self.create_ai_tab3(tab3)
        
        tab4 = ttk.Frame(nb)
        self.create_ai_tab4(tab4)
        
        nb.add(tab1, text='工作台')
        nb.add(tab2, text='接口配置')
        nb.add(tab3, text='系统设置')
        nb.add(tab4, text='关于项目')

    def change_style(self):
        """随机更改主题配色"""
        theme = random.choice(self.root.style.theme_names())
        self.root.style.theme_use(theme)
        #print(self.style.colors.get('primary')) #获得当前主题16进制颜色编码

    def create_ai_tab1(self,tab1):
        style = ttk.Style()
        self.aitextbox = ScrolledText(
            master=tab1,
            highlightcolor=style.colors.primary,
            highlightbackground=style.colors.border,
            highlightthickness=1,
            autohide=True
        )
        self.aitextbox.pack(fill=BOTH,expand=tk.YES)
        info = f"{config_dict['ainame']}：你好，我是你的智能助手，有什么想问的吗\n\n"
        self.aitextbox.insert(END, info)
        self.userque = ttk.StringVar()
        fr = ttk.Frame(tab1)
        fr.pack(side=tk.BOTTOM,fill=tk.BOTH,expand=tk.YES,padx=10,pady=10)
        userinput = ttk.Entry(fr,textvariable=self.userque)
        userinput.pack(side=tk.LEFT,fill=tk.X,expand=tk.YES)
        sendbtn = ttk.Button(fr,text="发送",command=self.send_userque)
        sendbtn.pack(side=tk.RIGHT,fill=tk.X)

    def send_userque(self):
        if self.userque.get() =="":
            messagebox.showinfo("提示","您没有输入任何问题哦！")
            return
        questr = f"我：{self.userque.get()}\n\n"
        self.aitextbox.insert(END, questr)
        question = SparkApi.checklen(SparkApi.getText("user",self.userque.get()))
        self.userque.set("")
        SparkApi.answer =""
        info = f"{config_dict['ainame']}："
        self.aitextbox.insert(END, info)
        t = threading.Thread(target=lambda:self.output_ai_return(question))
        t.daemon = True
        t.start()

    def output_ai_return(self,question):
        try:
            with open('apiconfig.json', mode='r', encoding='utf-8') as api:
                api_info = api.read()
                if api_info:
                    api_dict = json.loads(api_info)
        except FileNotFoundError:
            messagebox.showerror("出错","配置文件apiconfig.json不存在，请在【接口配置】重新配置！")
            return
        SparkApi.main(api_dict["appid"],api_dict["api_key"],api_dict["api_secret"],api_dict["spark_url"],api_dict["domain"],question,self.aitextbox)
        #self.typewriter_effect(SparkApi.answer, self.aitextbox)
        SparkApi.getText("assistant",SparkApi.answer)

    # 实现打字机效果的函数//已重写接口SparkApi函数，直接实现回调载入字符，实现所得及所见，毫秒相应。不需要再实现打字机效果
    def typewriter_effect(self,text, widget, delay=0.1):
        if text:
            widget.insert(tk.END, text[0]) #插入获得的第一个字符text[0]
            widget.see(tk.END)  # 确保滚动条滚动到最底部
            self.aitextbox.after(int(delay * 1000), self.typewriter_effect, text[1:], widget, delay) #使用字符串切片获取除第一个字符外的其他字符，知道遍历完全
        else:
            widget.insert(tk.END, "\n\n")

    def create_ai_tab2(self,tab2):
        #把变量self.appid = ttk.StringVar()声明为类变量，加上self，set方法才会生效，不然函数运行结束，变量会丢失
        
        self.appid = ttk.StringVar()
        self.api_secret = ttk.StringVar()
        self.api_key = ttk.StringVar()
        self.domain = ttk.StringVar()
        self.spark_url = ttk.StringVar()
        
        self.appid.set("填写控制台中获取的 APPID 信息")     #填写控制台中获取的 APPID 信息
        self.api_secret.set("填写控制台中获取的 APISecret 信息")   #填写控制台中获取的 APISecret 信息
        self.api_key.set("填写控制台中获取的 APIKey 信息")    #填写控制台中获取的 APIKey 信息
        self.domain.set("lite")         # Lite版本
        self.spark_url.set("wss://spark-api.xf-yun.com/v1.1/chat")  # Lite服务地址
        
        lbf = ttk.Labelframe(tab2,text="配置星火AI接口")
        lbf.pack(anchor="center",fill=tk.BOTH,padx=10,pady=10)
        
        fr1 = ttk.Frame(lbf)
        fr1.pack(side=tk.TOP,fill=tk.BOTH,padx=10,pady=10)
        ttk.Label(fr1,text="APPID",width=15).pack(side=tk.LEFT)
        e1 = ttk.Entry(fr1,textvariable=self.appid)
        e1.pack(side=tk.LEFT,fill=tk.X,expand=tk.YES)
        
        fr2 = ttk.Frame(lbf)
        fr2.pack(side=tk.TOP,fill=tk.BOTH,padx=10,pady=10)
        ttk.Label(fr2,text="API_SECRET",width=15).pack(side=tk.LEFT)
        e2 = ttk.Entry(fr2,textvariable=self.api_secret)
        e2.pack(side=tk.LEFT,fill=tk.X,expand=tk.YES)
        
        fr3 = ttk.Frame(lbf)
        fr3.pack(side=tk.TOP,fill=tk.BOTH,padx=10,pady=10)
        ttk.Label(fr3,text="API_KEY",width=15).pack(side=tk.LEFT)
        e3 = ttk.Entry(fr3,textvariable=self.api_key)
        e3.pack(side=tk.LEFT,fill=tk.X,expand=tk.YES)
        
        fr4 = ttk.Frame(lbf)
        fr4.pack(side=tk.TOP,fill=tk.BOTH,padx=10,pady=10)
        ttk.Label(fr4,text="DOMAIN",width=15).pack(side=tk.LEFT)
        e4 = ttk.Entry(fr4,textvariable=self.domain)
        e4.pack(side=tk.LEFT,fill=tk.X,expand=tk.YES)
        
        fr5 = ttk.Frame(lbf)
        fr5.pack(side=tk.TOP,fill=tk.BOTH,padx=10,pady=10)
        ttk.Label(fr5,text="SPARK_URL",width=15).pack(side=tk.LEFT)
        e5 = ttk.Entry(fr5,textvariable=self.spark_url)
        e5.pack(side=tk.LEFT,fill=tk.X,expand=tk.YES)

        fr6 = ttk.Frame(lbf)
        fr6.pack(side=tk.TOP,fill=tk.BOTH,padx=10,pady=10)
        ttk.Button(fr6,text="确定", bootstyle="success",command=self.on_submit).pack(side=tk.RIGHT,padx=10,pady=10)
        ttk.Button(fr6,text="重置", bootstyle="secondary",command=self.reset_all).pack(side=tk.RIGHT,padx=10,pady=10)

        
        

    def reset_all(self):
        self.appid.set("")
        self.api_secret.set("")
        self.api_key.set("")
        self.domain.set("")
        self.spark_url.set("")
        
    def on_submit(self):
        data = {"appid":self.appid.get(),"api_secret":self.api_secret.get(),"api_key":self.api_key.get(),"domain":self.domain.get(),"spark_url":self.spark_url.get()}
        for i in data.values():
            if " " in i or i == "":
                messagebox.showerror("失败","有空白项目或意外地输入了空格，请检查！")
                return False
                
        with open('apiconfig.json', mode='w', encoding='utf-8') as fp:
            text = json.dumps(data, ensure_ascii=False) #将python字典对象转换成json对象
            fp.write(text)
        messagebox.showinfo("成功","接口配置成功!")

    def create_ai_tab3(self,tab3):
        labellist = ["程序名称","自定义AI名称","窗口大小","程序图标","默认主题"]
        self.proname = ttk.StringVar()
        self.ainame = ttk.StringVar()
        self.windowsize = ttk.StringVar()
        self.windowicon = ttk.StringVar()
        self.defaulttheme = ttk.StringVar()
        self.proname.set(config_dict["proname"])
        self.ainame.set(config_dict["ainame"])
        self.windowsize.set(config_dict["windowsize"])
        self.windowicon.set(config_dict["windowicon"])
        self.defaulttheme.set(config_dict["defaulttheme"])
        varlist = [self.proname,self.ainame,self.windowsize,self.windowicon,self.defaulttheme]
        self.gen_form(tab3,labellist,varlist)
        

    def gen_form(self,tab3,labellist,varlist):
        formfr = ttk.Frame(tab3)
        formfr.pack(anchor="w")
        for i in range(len(labellist)):
            fr = ttk.Frame(formfr)
            fr.pack(side=tk.TOP,fill=tk.X,expand=tk.YES,pady=5)
            ttk.Label(fr,text=labellist[i],width=12).pack(side=tk.LEFT)
            ttk.Entry(fr,textvariable=varlist[i]).pack(side=tk.LEFT,fill=tk.X,padx=10)

        style = ttk.Style()
        theme_names = style.theme_names()
        theme_selection = ttk.Frame(formfr)
        theme_selection.pack(side=tk.TOP,fill=tk.X, expand=tk.YES,pady=5)

        lbl = ttk.Label(theme_selection, text="选择主题:",width=12)
        lbl.pack(side=tk.LEFT)
        self.theme_cbo = ttk.Combobox(
            master=theme_selection,
            text=style.theme.name,
            values=theme_names,
            state="readonly"
        )
        self.theme_cbo.pack(padx=10, side=tk.LEFT)
        self.theme_cbo.current(theme_names.index(style.theme.name))
        
        self.theme_cbo.bind("<<ComboboxSelected>>", self.change_theme)
        cs = ttk.Button(theme_selection,text='随机主题',bootstyle="outline-success",command=self.change_style).pack(side=tk.LEFT,padx=5)
        btnfr = ttk.Frame(formfr)
        btnfr.pack(side=tk.TOP,fill=tk.BOTH,padx=10,pady=10)

        ttk.Button(btnfr,text="恢复默认", bootstyle="secondary",command=self.recover_all).pack(side=tk.LEFT,padx=10,pady=10)
        ttk.Button(btnfr,text="确定设置", bootstyle="success",command=self.on_submit_setting).pack(side=tk.LEFT,padx=10,pady=10)
        

    def change_theme(self,b=None):
        #print(self.theme_cbo.get())
        self.root.style.theme_use(self.theme_cbo.get())

    def on_submit_setting(self):
        setting_dict = {"proname":self.proname.get(),"ainame":self.ainame.get(),"windowsize":self.windowsize.get(),"windowicon":self.windowicon.get(),"defaulttheme":self.defaulttheme.get()}
        for i in setting_dict.values():
            if " " in i or i == "":
                messagebox.showerror("失败","有空白项目或意外地输入了空格，请检查！")
                return False
                
        with open('userconfig.json', mode='w', encoding='utf-8') as fp:
            text = json.dumps(setting_dict, ensure_ascii=False) #将python字典对象转换成json对象
            fp.write(text)
        msg = messagebox.askokcancel("成功","设置成功!，确定退出后重新启动应用可以显示！")
        if msg:
            self.root.destroy()
            self.root.quit()
        

    def recover_all(self):
        self.proname.set("星火AI")
        self.ainame.set("星火")
        self.windowsize.set("1200x670")
        self.windowicon.set(".\winicon.png")
        self.defaulttheme.set("superhero")

    def create_ai_tab4(self,tab4):
        self.textbox = ScrolledText(
            master=tab4,
            autohide=True
        )
        self.textbox.pack(fill=BOTH,expand=tk.YES)
        aboutus = '''本程序基于笔者项目多源异构数据存储管理系统V2.0剥离而出。\n
API接口使用星火官方提供的。你需要从官网申请到你的AI模型并在【接口配置】中成功配置方可使用。\n
申请链接：https://xinghuo.xfyun.cn/sparkapi?ch=api_ba。\n
lite版本免费无限token,详细信息可以查看星火官方文档：https://www.xfyun.cn/doc/spark/Web.html。\n
是一个使用 ttkbootstrap 构建的开源桌面应用程序，它通过 WebSocket 协议与星火API进行实时通信。\n
该程序提供了一个直观的用户界面，使得用户可以轻松地连接到星火API，发送和接收数据，而无需直接处理复杂的网络编程。\n
功能特点\n
实时通信：利用 WebSocket 协议实现与星火API的实时双向通信。\n
简洁的用户界面：使用 ttkbootstrap 构建，提供现代且响应式的用户体验。\n
数据管理：能够查看、编辑和管理从星火API接收的数据。\n
错误处理：友好的错误提示和异常处理机制。\n
多平台支持：可在Windows、macOS和Linux上运行。\n
技术栈\n
Python：主要编程语言。\n
ttkbootstrap：用于构建GUI。\n
websocket-client：用于实现 WebSocket 客户端功能。\n
JSON：用于处理API响应数据。\n
开源许可\n
本项目遵循 MIT License。你可以自由地使用、复制、修改和分发代码，只要遵循许可协议。\n
欢迎关注云墨北微信公众号sharecho了解更多！联系我们：1346363063，站长号: 160540168。'''
        self.textbox.insert(tk.END,aboutus)
        self.textbox._text.configure(state="disabled")

if __name__ == "__main__":
    try:
        with open('userconfig.json', mode='r', encoding='utf-8') as fp:
            fp_info = fp.read()
            if fp_info:
                config_dict = json.loads(fp_info) #将Json字符串解码成python对象   
    except FileNotFoundError:
        config_dict = {"proname":"星火AI","ainame":"星火","windowsize":"1200x670","windowicon":".\winicon.png","defaulttheme":"vapor"}
    app = ttk.Window(iconphoto=config_dict["windowicon"], alpha=1.0)
    SparkAi(app)
    app.mainloop()
